<template>
  <div class="full">
    <div id="imChat">
      <el-container>
        <!-- 侧边栏 -->
        <el-aside class="chat-sidebar">
          <!-- 搜索框-->
          <div>
            <el-input
              v-model="search"
              clearable
              placeholder="搜索联系人"
              style="padding: 10px; touch-action: none"
              @change="handelSearch"
              @clear.passive="getContactList"
            />
          </div>

          <!-- 联系人列表-->
          <el-scrollbar max-height="555">
            <empty-view
              v-if="contactList.length === 0"
              description="没有查找到联系人"
            />
            <div v-else>
              <div
                v-for="item in contactList as ContactVO[] "
                :key="item.id"
                :class="[
                  inContact === item.id ? 'chat-contact-on' : '',
                  item.isTop ? 'chat-contact-unshift' : '',
                ]"
                class="chat-contact"
                @click="changeContact(item!.id as number)"
                @contextmenu="
                  useContextMenu($event, contactContextMenu(item as any))
                "
              >
                <el-row :gutter="10">
                  <!-- 头像-->
                  <el-col :span="4">
                    <el-badge :show-zero="false" :value="item.unread as number">
                      <el-avatar
                        :src="item.avatar || LOGO"
                        lazy
                        shape="square"
                      />
                    </el-badge>
                  </el-col>
                  <!-- 右边-->
                  <el-col :span="20">
                    <div style="width: 100%; display: flex">
                      <el-text class="chat-contact-name" truncated>
                        {{ item.nickname }}
                      </el-text>
                      <el-text class="chat-contact-time" size="small">
                        {{ getHistoryTime(parseInt(item.lastSendTime)) }}
                      </el-text>
                    </div>

                    <!-- 内容-->
                    <el-text class="chat-contact-content" size="small" truncated
                      >{{ item.lastContent }}
                    </el-text>
                  </el-col>
                </el-row>
              </div>
            </div>
          </el-scrollbar>
        </el-aside>
        <!-- 右边-->
        <el-container v-if="currentContact" class="chat-main">
          <el-header class="chat-main-header">
            <el-text>{{ currentContact?.nickname }}</el-text>
          </el-header>
          <el-main class="chat-main-message">
            <el-scrollbar
              ref="messageScrollbarRef"
              height="355"
              max-height="385"
            >
              <div ref="innerRef">
                <loading-page v-show="isMessageLoading" />
                <el-text
                  v-show="!isMessageLoading && currentMessage.length === 0"
                  size="small"
                  style="display: block; text-align: center"
                  >暂无消息
                </el-text>
                <div v-for="(item, index) in currentMessage" :key="item.id">
                  <!-- 时间提示 -->
                  <div
                    v-if="
                      index > 0 &&
                      showEventTime(
                        currentMessage[index - 1].sendTime,
                        item.sendTime
                      )
                    "
                    class="message-content-event-time"
                  >
                    <span>
                      {{ getEventTime(item?.sendTime) }}
                    </span>
                  </div>
                  <!-- 如果发送人是当前用户，反转-->
                  <div
                    :class="
                      item.from.id === user.id ? ' chat-message-reverse' : ''
                    "
                    class="chat-message"
                  >
                    <template v-if="item.type !== 'event'">
                      <!-- 左侧头像-->
                      <div class="message-avatar">
                        <el-avatar
                          :src="item.from.avatar || LOGO"
                          lazy
                          shape="square"
                        />
                      </div>
                      <!-- 右侧内容-->
                      <div class="message-inner">
                        <!--<div class="message-title">-->
                        <!--  <span>{{-->
                        <!--    item.from.id !== user.id-->
                        <!--      ? item.to.nickname-->
                        <!--      : user.nickname-->
                        <!--  }}</span>-->
                        <!--  <span class="message-time">{{-->
                        <!--    getHistoryTime(item.sendTime)-->
                        <!--  }}</span>-->
                        <!--</div>-->
                        <!-- 聊天内容-->
                        <div class="message-content-main">
                          <div
                            v-if="item.type === 'text'"
                            class="message-content-text"
                            @contextmenu="
                              useContextMenu($event, msgContextMenu(item))
                            "
                          >
                            {{ item.content }}
                          </div>

                          <el-image
                            v-else-if="item.type === 'image'"
                            :preview-src-list="[item.content as string]"
                            :src="item.content"
                            class="message-content-image"
                            lazy
                          >
                            <template #error>
                              <div class="image-slot">
                                <el-icon>
                                  <icon-picture />
                                </el-icon>
                              </div>
                            </template>
                          </el-image>
                          <div class="message-content-after">
                            <span
                              v-if="item.status === MessageStatusEnum.going"
                              class="icon-jiazai el-icon is-loading"
                            ></span>
                            <el-space
                              v-if="item.status === MessageStatusEnum.failed"
                              size="small"
                            >
                              <el-button
                                size="small"
                                text
                                type="primary"
                                @click="reSend(item)"
                                >重新发送
                              </el-button>
                              <span class="icon-71shibai"></span>
                            </el-space>
                          </div>
                        </div>
                      </div>
                    </template>
                    <template v-else>
                      <div class="message-content-event">
                        <span>
                          {{ item.from.id === user.id ? "你" : "对方"
                          }}{{ item.content }}
                          <el-button
                            v-if="handelResetShow(item.sendTime)"
                            link
                            type="primary"
                            @click="reInput"
                            >重新编辑</el-button
                          >
                        </span>
                      </div>
                    </template>
                  </div>
                </div>
              </div>
            </el-scrollbar>
          </el-main>
          <el-footer class="chat-main-editor">
            <message-editor
              :text="resetText"
              @send:message="handleSendMessage"
            />
          </el-footer>
        </el-container>
        <el-container v-else class="chat-main-empty">
          <div class="logo">
            <el-image :src="LOGO" />
          </div>
          <el-config-provider size="large">
            <el-text style="font-size: 32px" tag="b"
              >欢迎使用云选购物系统
            </el-text>
            <el-text type="primary">云选</el-text>
            <el-text type="primary">云上选购</el-text>
            <el-text type="primary">甄选品质</el-text>
          </el-config-provider>
        </el-container>
      </el-container>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { Picture as IconPicture } from "@element-plus/icons-vue";
import LOGO from "@/assets/logo.png";
import { nextTick, onUnmounted, ref } from "vue";
import LoadingPage from "@/components/chat/LoadingPage.vue";
import MessageEditor from "@/components/chat/MessageEditor.vue";
import { ElMessage, ElMessageBox, ElScrollbar } from "element-plus";
import useContextMenu from "@/components/chat/useContextMenu";
import {
  getEventTime,
  getHistoryTime,
  getUUID,
  showEventTime,
} from "@/util/BasicUtil";
import { MenuItem } from "@imengyu/vue3-context-menu/lib/ContextMenuDefine";
import { useUserStore, useWebSocketStore } from "@/store";
import {
  ChatControllerService,
  ContactVO,
  MessageVO,
  SimpleContactVO,
} from "@/api";
import { useRoute, useRouter } from "vue-router";
//消息右键事件
import useClipboard from "vue-clipboard3";
import { MessageStatusEnum } from "@/types/MessageStatusEnum";
import EmptyView from "@/components/EmptyView.vue";

//获取路由参数
const route = useRoute();
const userStore = useUserStore();
const userInfo = userStore.userInfo;
//当前登录用户信息
const user = ref<SimpleContactVO>({});
//获取路由
const router = useRouter();
if (userInfo?.id) {
  window.addEventListener("onmessageWS", onmessage);
  user.value = {
    id: userInfo.id,
    avatar: userInfo.avatar,
    nickname: userInfo.nickname,
  } as SimpleContactVO;
  //初始化联系人
  initContact();
} else {
  router.back();
}

//信息加载标志
const isMessageLoading = ref(false);
//websocket对象
const webSocket = useWebSocketStore();

//处理接收到的数据
function onmessage(value: Event) {
  const messageStr = (value?.detail.data as string).trim();
  if (messageStr.startsWith("{")) {
    //消息
    if (messageStr.includes("status")) {
      const message: MessageVO = JSON.parse(messageStr);
      addMessage(message);
    } else if (messageStr.includes("code")) {
      // 后端响应结果
      const res = JSON.parse(messageStr);
      if (res.type === "message") {
        changeMessageStatus(res.data, res.code);
      } else {
        if (res.code === 201) ElMessage.error("更新联系人信息失败！");
      }
    }
  }
}

// 联系人开始
//当前选择的联系人Id
const inContact = ref(0);
//当前联系人
const currentContact = ref<ContactVO>();
// 联系人列表
const contactList = ref<Array<ContactVO>>([]);
// 搜索联系人
const search = ref("");
const timeout = ref();

const getContactList = () => {
  nextTick(() => {
    ChatControllerService.getContactListUsingGet(search.value).then((res) => {
      contactList.value = res.data || [];
      contactSort(); //排序
    });
  });
};

function handelSearch() {
  if (search.value.trim().length === 0) return;
  clearTimeout(timeout.value);
  timeout.value = setTimeout(() => {
    getContactList();
  }, 500);
}

//联系人右键事件
function contactContextMenu(value: ContactVO) {
  const menuItem = [];
  //判断是否要置顶
  menuItem.push(
    !value.isTop
      ? {
          label: "置顶",
          onClick: () => changeTop(value.id as number),
        }
      : {
          label: "取消置顶",
          onClick: () => changeTop(value.id as number),
        }
  );
  //是否要标记未读
  menuItem.push(
    (value.unread as number) > 0
      ? {
          label: "标记已读",
          onClick: () => {
            value.unread = 0;
          },
        }
      : {
          label: "标记未读",
          onClick: () => {
            value.unread = 1;
          },
        }
  );
  menuItem.push({
    label: "删除",
    onClick: () => deleteContact(value.id as number),
  });

  return menuItem;
}

// 初始化联系人
async function initContact() {
  const businessId: any = route.query.businessId;
  //有商家id,获取商家编号
  if (businessId) {
    await ChatControllerService.addContactUsingPost(businessId);
  }
  ChatControllerService.getContactListUsingGet().then((res) => {
    contactList.value = res.data || [];
    contactSort(); //排序
    //如果Id存在，切换置联系人
    if (businessId) {
      changeContact(businessId);
    }
  });
}

// 联系人排序
function contactSort() {
  //按时间倒序
  contactList.value.sort((a, b) => {
    if (a.isTop) {
      return -1;
    }
    if (b.isTop) {
      return b.isTop;
    }
    return (
      parseInt(b.lastSendTime as string) - parseInt(a.lastSendTime as string)
    );
  });
  console.log(contactList.value);
}

// 消息排序
function messageSort() {
  //按时间倒序
  currentMessage.value.sort((a, b) => {
    return parseInt(a.sendTime as any) - parseInt(b.sendTime as any);
  });
}

//改变置顶
function changeTop(id: number) {
  const contact = contactList.value.find((item) => item.id === id);
  if (contact) {
    //取反
    contact.isTop = contact.isTop ? 0 : 1;

    ChatControllerService.updateContactTopStatusUsingPut(
      contact.id,
      contact.isTop
    ).then(() => {
      console.log("修改成功！");
    });
  }
  //置顶
  // contactList.value.unshift(
  //   contactList.value.splice(
  //     contactList.value.findIndex((i) => i.id === id),
  //     1
  //   )[0]
  // );
  //排序
  contactSort();
}

// 删除联系人
function deleteContact(id: number) {
  ElMessageBox.confirm("确定要删除该联系人吗?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      ChatControllerService.deleteContactUsingDelete(id).then((res) => {
        let index = contactList.value.findIndex((item) => (item.id = id));
        contactList.value.splice(index, 1);
        ElMessage.success(res.data);
      });
    })
    .catch(() => {
      //
    });
}

// 切换联系人
async function changeContact(id: number) {
  //加载中
  isMessageLoading.value = true;
  //获取当前联系人信息
  const select: ContactVO =
    contactList.value.find((item) => item.id === id) || {};
  if (!select) return;
  //切换当前联系人
  currentContact.value = select;
  //修改未读数为0
  currentContact.value.unread = 0;
  //切换选中的Id
  inContact.value = id;
  webSocket.setContactId(id);
  //判断是否已存在信息
  if (!messageList.value.has(id)) {
    // 不存在，远程查信息
    await ChatControllerService.getMessageListUsingGet(id).then((res) => {
      const messages = res.data || [];
      messageList.value.set(select.id as number, messages);
    });
  }
  //切换获取的信息
  currentMessage.value =
    (messageList.value?.get(select.id as number) as MessageVO[]) || [];
  isMessageLoading.value = false;
  // 对消息排序
  messageSort();
  //远程更新联系人信息
  webSocket.send(JSON.stringify(currentContact.value));
  //置底
  setScrollTop();
}

//联系人结束

//全部信息列表
const messageList = ref<Map<number, MessageVO[]>>(
  new Map<number, MessageVO[]>()
);
//当前窗口消息
const currentMessage = ref<MessageVO[]>([]);

//根据消息获取联系人Id
function getContactIdByMessage(message: MessageVO) {
  if (message)
    return message?.from!.id === user.value.id
      ? message?.to?.id
      : message?.from?.id;
  return 0;
}

//更新联系人信息
function updateContact(message: MessageVO) {
  if (!message) return;
  //获取联系人Id
  const contactId = getContactIdByMessage(message);
  //找到联系人
  const contact = contactList.value.find((item) => item.id === contactId);
  if (contact) {
    //更新最后一条信息
    contact!.lastSendTime = message.sendTime as any;
    contact!.lastContent = getLastContent(message);
    if (contact.id !== inContact.value) {
      contact.unread = (contact.unread as number) + 1;
    }
    //向后端更新联系人信息
    webSocket.send(JSON.stringify(contact));
  }
}

function getLastContent(message: MessageVO) {
  switch (message.type) {
    case "text":
      return message.content;
    case "image":
      return "[图片]";
    default: {
      const prefix = message?.from?.id === user.value.id ? "你" : "对方";
      return prefix + message.content;
    }
  }
}

//添加消息
function addMessage(value: MessageVO) {
  if (!value) return;
  //获取联系人Id
  const contactId = getContactIdByMessage(value) || 0;
  // 更新联系人信息
  updateContact(value);
  // 如果消息列表不存在，则直接返回
  if (!messageList.value.has(contactId)) return;
  const messages = messageList.value.get(contactId) as MessageVO[];
  //判断该信息Id是否已存在
  const index = messages.findIndex((item) => item.id === value.id);
  if (index != -1) {
    messages[index] = { ...value };
  } else {
    //添加消息
    messages.push(value);
  }

  //如果在当前用户，则置底
  if (contactId === currentContact.value?.id) {
    // 时间排序
    messageSort();
    setScrollTop();
  }
}

// 发送消息
function handleSendMessage(value: string, type?: string) {
  // let message: MessageVO = {
  //   id: getUUID(),
  //   type: !type ? "text" : type,
  //   sendTime: new Date().getTime(),
  //   content: value,
  //   from: currentContact.value,
  //   to: user.value,
  //   status: "going",
  // };
  const message: MessageVO = {
    id: getUUID(),
    type: !type ? "text" : type,
    sendTime: new Date().getTime(),
    content: value,
    to: {
      id: currentContact.value?.id,
      nickname: currentContact.value?.nickname,
      avatar: currentContact.value?.avatar,
    },
    from: user.value,
    status: "going",
  };
  addMessage(message);
  // 设置延迟消息,5秒内发送失败就改变状态
  const timeOut = setTimeout(() => {
    const data = currentMessage.value.find((item) => item.id === message.id);
    if (data && data.status !== MessageStatusEnum.succeed) {
      ElMessage.warning("发送失败，请刷新网页或者重新发送");
      data.status = MessageStatusEnum.failed;
    }
    clearTimeout(timeOut);
  }, 1000 * 5);

  webSocket.send(JSON.stringify(message));
}

//重新发送
function reSend(message: MessageVO) {
  message.status = MessageStatusEnum.going;
  addMessage(message);
  // 设置延迟消息,5秒内发送失败就改变状态
  const timeOut = setTimeout(() => {
    const data = currentMessage.value.find((item) => item.id === message.id);
    if (data && data.status !== MessageStatusEnum.succeed) {
      ElMessage.warning("发送失败，请刷新网页或者重新发送");
      data.status = MessageStatusEnum.failed;
    }
    clearTimeout(timeOut);
  }, 1000 * 5);

  webSocket.send(JSON.stringify(message));
}

function changeMessageStatus(id: string, code: number) {
  const message = currentMessage.value.find((item) => item.id === id);
  if (message) {
    message.status =
      code === 200 ? MessageStatusEnum.succeed : MessageStatusEnum.failed;
  }
}

const { toClipboard } = useClipboard();
const messageInput = ref("");

// 判断是否展示（两分钟内展示）
function handelResetShow(time: number) {
  const endTime = new Date().getTime();
  return Math.floor((endTime - time) / (2 * 60 * 1000)) <= 0;
}

function msgContextMenu(value: MessageVO) {
  const menuItem: MenuItem[] = [];
  if (value.type === "text") {
    menuItem.push({
      icon: "icon-duixiang",
      iconFontClass: "icon-style",
      label: "复制消息",
      onClick: async () => {
        try {
          await toClipboard(value.content as string);
          ElMessage.success("复制成功！");
        } catch (e) {
          console.error(e);
          ElMessage.error("复制失败！");
        }
      },
    });
  }

  menuItem.push({
    label: "删除信息",
    onClick: () => {
      ElMessageBox.confirm("确定要删除该消息吗?", "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          deleteMessage(value.id as string);
        })
        .catch(() => {
          //
        });
    },
  });
  //用户两分钟内可撤回
  const endTime = new Date().getTime();
  if (
    value?.from?.id === user.value.id &&
    Math.floor((endTime - (value.sendTime as number)) / (2 * 60 * 1000)) <= 0
  ) {
    menuItem.push({
      label: "撤回",
      onClick: () => {
        //填充到待输入区域
        messageInput.value = value.content as string;
        //删除消息
        deleteMessage(value.id as string);
        const message: MessageVO = {
          id: getUUID(),
          status: "going",
          type: "event",
          sendTime: endTime,
          content: "撤回了一条信息",
          to: currentContact.value,
          from: user.value,
        };
        addMessage(message);
        // 需要发送
        webSocket.send(JSON.stringify(message));
      },
    });
  }
  return menuItem;
}

// 重新编辑
const resetText = ref("");

function reInput() {
  if (messageInput.value) {
    resetText.value = messageInput.value + "";
  } else {
    ElMessage.warning("没有填充内容，无法重新编辑！");
  }
}

//删除消息
function deleteMessage(id: string) {
  ChatControllerService.deleteMessageUsingDelete(id).then((res) => {
    currentMessage.value?.splice(
      currentMessage.value?.findIndex((item) => item.id === id),
      1
    );
    nextTick(() => {
      ElMessage.success(res.data);
    });
  });
}

//消息滚动条
const messageScrollbarRef = ref<InstanceType<typeof ElScrollbar>>();
const innerRef = ref();

// 置底
function setScrollTop() {
  nextTick(() => {
    //数据渲染后置底
    messageScrollbarRef.value?.setScrollTop(innerRef.value?.clientHeight);
  });
}

onUnmounted(() => {
  if (webSocket.contactId) {
    webSocket.setContactId(0);
  }
});
</script>

<style lang="scss" scoped>
#imChat {
  border-radius: 10px;
  width: 80%;
  height: 80%;
  margin: 30px auto;
  overflow: auto;
  background-color: #eae8e7;

  .chat-sidebar {
    width: 280px;
    height: 100%;
    overflow: hidden;

    .chat-contact {
      padding: 8px;

      &:hover {
        background-color: #d9d9d8;
      }

      .chat-contact-name {
        width: 120px;
      }

      .chat-contact-time {
        margin: 0 auto;
        color: #999999;
      }

      .chat-contact-content {
        color: #999999;
        width: 200px;
      }
    }
  }

  .chat-main {
    background-color: #f5f5f5;
    display: flex;

    .chat-main-header {
      height: 52px;
      display: flex;
      align-items: center;
      font-size: 20px;
      font-weight: bolder;
    }

    .chat-main-message {
      flex: 1;
      border-top: solid 1px #e7e7e7;
      border-bottom: solid 1px #e7e7e7;

      // 时间event
      .message-content-event-time {
        text-align: center;

        span {
          background-color: #dadada;
          color: #ffffff;
          font-size: 14px;
          padding: 2px 5px;
        }
      }

      .chat-message {
        display: flex;
        padding: 10px 0;

        .message-avatar {
          padding-right: 10px;
          width: 36px;
          height: 36px;
        }

        .message-inner {
          max-width: 70%;

          .message-title {
            color: #666666;
            font-size: 12px;
            padding-bottom: 4px;
            height: 16px;

            .message-time {
              color: #999999;
            }
          }

          .message-content-main {
            display: flex;

            .message-content-image {
              width: 150px;
              border-radius: 10px;
              background: #eae8e7;

              .image-slot {
                display: flex;
                justify-content: center;
                align-items: center;

                color: var(--el-text-color-secondary);
                font-size: 30px;
                margin: 50px;

                .el-icon {
                  font-size: 30px;
                }
              }
            }

            .message-content-text {
              flex: 1;
              //max-width: 70%;
              font-size: 14px;
              line-height: 20px;
              padding: 8px 10px;
              background: #fff;
              border-radius: 4px;
              position: relative;
              margin: 0;
              // 换行
              //word-wrap: break-word;
              //overflow-wrap: break-word;
              //white-space: pre-wrap;

              word-wrap: break-word;
              word-break: break-all;

              &::before {
                content: " ";
                position: absolute;
                top: 13px;
                width: 0;
                height: 0;
                border: 4px solid transparent;
                left: -4px;
                border-left: none;
                border-right-color: #fff;
              }
            }

            .message-content-after {
              display: flex;
              justify-content: center;
              padding-left: 6px;

              span {
                margin: auto 0;
              }

              .icon-jiazai::before {
                color: red;
              }

              .icon-71shibai::before {
                color: red;
              }
            }
          }
        }

        .message-content-event {
          color: #999999;
          font-size: 12px;
          padding: 2px 5px;
          margin: 0 auto;
        }
      }

      .chat-message-reverse {
        // 反转
        flex-direction: row-reverse;

        .message-avatar {
          padding-left: 10px;
        }

        .message-inner {
          .message-title {
            flex-direction: row-reverse;
            display: flex;
          }

          .message-content-main {
            flex-direction: row-reverse;

            .message-content-text {
              background: #35d863;

              &::before {
                content: " ";
                position: absolute;
                top: 13px;
                width: 0;
                height: 0;
                border: 4px solid transparent;
                left: auto;
                right: -4px;
                border-right: none;
                border-left-color: #35d863;
              }
            }

            .message-content-after {
              flex-direction: row-reverse;
              padding-right: 6px;
            }
          }
        }
      }
    }

    .chat-main-editor {
      height: 180px;
    }
  }

  .chat-main-empty {
    min-height: 610px;
    background-color: #f5f5f5;
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;

    .logo {
      display: flex;
      place-content: center center;
      height: 200px;
      padding: 10px;
    }
  }
}

.chat-contact-on {
  background-color: #c5c4c4 !important;
}

.chat-contact-unshift {
  background-color: #e0dedd;
}

:deep(.el-main) {
  padding: 10px 20px;
}

:deep(.el-badge__content) {
  font-size: 8px !important;
  height: 10px !important;
  width: 10px;
  padding: 3px;
  border: none;
}

:deep(.mx-context-menu) {
  padding: 6px 0 !important;
}

:deep(.mx-context-menu-item) {
  padding: 6px 5px !important;
}
</style>
